#ifndef __H_SYNCHRONIZER__
#define __H_SYNCHRONIZER__

namespace bl{

class CritSec
{
	// Private to avoid accidental use
	CritSec(const CritSec &refCritSec);
	CritSec &operator=(const CritSec &refCritSec);

public:
#ifdef _DEBUG
	UINT32 current_owner_;
	UINT32 lock_count_;
	bool is_trace_;

	CritSec() : current_owner_(0), lock_count_(0), is_trace_(false) { ::InitializeCriticalSection(&cs_); }
	~CritSec() { ::DeleteCriticalSection(&cs_); }
	void Lock()
	{
		DWORD myid = GetCurrentThreadId();
		DWORD owner = current_owner_;
		if( owner && (owner!=myid))
		{
			// Already owned by other thread
			if(is_trace_)
				::OutputDebugString(TEXT("We need to wait because the object was locked by others before\n"));
		}
		::EnterCriticalSection(&cs_);
		if(0== lock_count_++)
		{
			current_owner_ = myid;
			if(is_trace_)
				::OutputDebugString(TEXT("We own the critical section first time\n"));
		}
	}

	void Unlock()
	{
		if( 0== --lock_count_)
		{
			if(is_trace_)
				::OutputDebugString(TEXT("We are relasing the lock to critical section\n"));
			current_owner_ = 0;
		}
		::LeaveCriticalSection(&cs_);
	}
#else
	CritSec()
	{
		::InitializeCriticalSection(&cs_);
	}

	~CritSec()
	{
		::DeleteCriticalSection(&cs_);
	}

	void Lock()
	{
		::EnterCriticalSection(&cs_);
	}

	void Unlock()
	{
		::LeaveCriticalSection(&cs_);
	}
#endif

private:
	CRITICAL_SECTION cs_;
};

template<class TLock>
class TAutoLock
{
public:
	TAutoLock(TLock& obj) : lock_(obj)
	{
		lock_.Lock();
	}

	~TAutoLock()
	{
		lock_.Unlock();
	}

private:
	TLock& lock_;

	// Private to avoid accidental use
	TAutoLock(const TAutoLock &lock);
	TAutoLock &operator=(const TAutoLock &lock);
};

typedef TAutoLock<CritSec> AutoLockCS;






// Event

class Event
{
public:
	Event(bool manualReset, bool initialState)
	{
		event_ = ::CreateEvent( NULL, manualReset, initialState, NULL );
	}

	~Event() {}

	operator HANDLE() const
	{
		return event_;
	}

	bool Set()
	{
		return !!::SetEvent(event_);
	}

	bool Reset()
	{
		return !!::ResetEvent(event_);
	}

	bool Pulse()
	{
		return !!::PulseEvent(event_);
	}

	bool Wait(unsigned long msec = INFINITE)
	{
		if (WAIT_OBJECT_0 == ::WaitForSingleObject(event_, msec) )
			return true;

		return false;
	}
	
	HANDLE event_;

private:
	Event();
};

inline BOOL WaitWithMessageLoop(HANDLE hEvent)
{
	DWORD dwRet;
	MSG msg;

	while(1)
	{
		dwRet = MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT);

		if (dwRet == WAIT_OBJECT_0)
			return TRUE;    // The event was signaled

		if (dwRet != WAIT_OBJECT_0 + 1)
			break;          // Something else happened

		// There is one or more window message available. Dispatch them
		while(PeekMessage(&msg,0,0,0,PM_NOREMOVE))
		{
			// check for unicode window so we call the appropriate functions
			BOOL bUnicode = ::IsWindowUnicode(msg.hwnd);
			BOOL bRet;

			if (bUnicode)
				bRet = ::GetMessageW(&msg, NULL, 0, 0);
			else
				bRet = ::GetMessageA(&msg, NULL, 0, 0);

			if (bRet > 0)
			{
				::TranslateMessage(&msg);

				if (bUnicode)
					::DispatchMessageW(&msg);
				else
					::DispatchMessageA(&msg);
			}

			if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0)
				return TRUE; // Event is now signaled.
		}
	}
	return FALSE;
}


}

#endif